/*
 * Copyright (c) 2022 Winner Microelectronics Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file    wm_crypto_hard.h
 *
 * @brief   hard crypto module
 *
 * @author  dave
 *
 * Copyright (c) 2014 Winner Microelectronics Co., Ltd.
 */

#ifndef WM_CRYPTO_HARD_H
#define WM_CRYPTO_HARD_H

#include "wm_type_def.h"
#include "wm_osal.h"

#ifndef min
    #define min(a, b)    (((a) < (b)) ? (a) : (b))
#endif /* min */

/** The base address of the registers of encryption/decryption module. */
#define HR_CRYPTO_BASE_ADDR   0x40000600
/** The address of the source text address register. */
#define HR_CRYPTO_SRC_ADDR     (HR_CRYPTO_BASE_ADDR + 0x00)
/** The address of the encrypted/decrypted text address register. */
#define HR_CRYPTO_DEST_ADDR     (HR_CRYPTO_BASE_ADDR + 0x04)
/** The address of the configuration register. */
#define HR_CRYPTO_SEC_CFG     (HR_CRYPTO_BASE_ADDR + 0x08)
/** The address of the control register. */
#define HR_CRYPTO_SEC_CTRL     (HR_CRYPTO_BASE_ADDR + 0x0C)
/** The address of the key0 register. */
#define HR_CRYPTO_KEY0     (HR_CRYPTO_BASE_ADDR + 0x10)
/** The address of the key1 register. */
#define HR_CRYPTO_KEY1     (HR_CRYPTO_BASE_ADDR + 0x14)
/** The address of the key2 register. */
#define HR_CRYPTO_KEY2     (HR_CRYPTO_BASE_ADDR + 0x18)
/** The address of the key3 register. */
#define HR_CRYPTO_KEY3     (HR_CRYPTO_BASE_ADDR + 0x1C)
/** The address of the key4 register. */
#define HR_CRYPTO_KEY4     (HR_CRYPTO_BASE_ADDR + 0x20)
/** The address of the key5 register. */
#define HR_CRYPTO_KEY5     (HR_CRYPTO_BASE_ADDR + 0x24)
/** The address of the key6 register. */
#define HR_CRYPTO_KEY6     (HR_CRYPTO_BASE_ADDR + 0x4c)
/** The address of the key7 register. */
#define HR_CRYPTO_KEY7     (HR_CRYPTO_BASE_ADDR + 0x50)

/** The address of the IV0 register. */
#define HR_CRYPTO_IV0       (HR_CRYPTO_BASE_ADDR + 0x28)
/** The address of the IV1 register. */
#define HR_CRYPTO_IV1       (HR_CRYPTO_BASE_ADDR + 0x20)

/** The address of the encrypted/decrypted status register. */
#define HR_CRYPTO_SEC_STS     (HR_CRYPTO_BASE_ADDR + 0x30)

/** The address of the SHA1/MD5 digest0 register. */
#define HR_CRYPTO_SHA1_DIGEST0     (HR_CRYPTO_BASE_ADDR + 0x34)
/** The address of the SHA1/MD5 digest1 register. */
#define HR_CRYPTO_SHA1_DIGEST1     (HR_CRYPTO_BASE_ADDR + 0x38)
/** The address of the SHA1/MD5 digest2 register. */
#define HR_CRYPTO_SHA1_DIGEST2     (HR_CRYPTO_BASE_ADDR + 0x3C)
/** The address of the SHA1/MD5 digest3 register. */
#define HR_CRYPTO_SHA1_DIGEST3     (HR_CRYPTO_BASE_ADDR + 0x40)
/** The address of the SHA1 digest4 and CRC in/output register. */
#define HR_CRYPTO_SHA1_DIGEST4     (HR_CRYPTO_BASE_ADDR + 0x44)
/** The address of the RNG RESULT register. */
#define HR_CRYPTO_RNG_RESULT     (HR_CRYPTO_BASE_ADDR + 0x48)
/** The address of the TRNG CR register. */
#define HR_CRYPTO_TRNG_CR        (HR_CRYPTO_BASE_ADDR + 0x54)

/** The address of the CRC in register. */
#define HR_CRYPTO_CRC_KEY   HR_CRYPTO_SHA1_DIGEST4
/** The address of the CRC output register. */
#define HR_CRYPTO_CRC_RESULT    HR_CRYPTO_SHA1_DIGEST4

#define ERR_CRY_OK           0    // /< No error, everything OK.
#define ERR_FAILURE         (-1)    /* failure */
#define ERR_ARG_FAIL        (-6)    /* Failure due to bad function param */

# define PS_SUCCESS          0
# define PS_FAILURE         (-1)
# define PS_FAIL             PS_FAILURE /* Just another name */

/*      NOTE: Failure return codes MUST be < 0 */
/*      NOTE: The range for core error codes should be between -2 and -29 */
# define PS_ARG_FAIL         (-6)      /* Failure due to bad function param */
# define PS_PLATFORM_FAIL    (-7)      /* Failure as a result of system call error */
# define PS_MEM_FAIL         (-8)      /* Failure to allocate requested memory */
# define PS_LIMIT_FAIL       (-9)      /* Failure on sanity/limit tests */
# define PS_UNSUPPORTED_FAIL (-10)      /* Unimplemented feature error */
# define PS_DISABLED_FEATURE_FAIL (-11) /* Incorrect #define toggle for feature */
# define PS_PROTOCOL_FAIL    (-12)      /* A protocol error occurred */
# define PS_TIMEOUT_FAIL     (-13)      /* A timeout occurred and MAY be an error */
# define PS_INTERRUPT_FAIL   (-14)      /* An interrupt occurred and MAY be an error */
# define PS_PENDING          (-15)      /* In process. Not necessarily an error */
# define PS_EAGAIN           (-16)      /* Try again later. Not necessarily an error */
# define PS_OUTPUT_LENGTH    (-17)      /* Output length negotiation:
                                         output buffer is too small. */
# define PS_HOSTNAME_RESOLUTION (-18)   /* Cannot resolve host name. */
# define PS_CONNECT             (-19)    /* Cannot connect to remote host. */
# define PS_INSECURE_PROTOCOL   (-20)   /* The operation needs to use insecure protocol.
                                         The caller needs to accept use of insecure
                                         protocol. */
# define PS_VERIFICATION_FAILED (-21)   /* Signature verification failed. */

// CRC
#define OUTPUT_REFLECT 1
#define INPUT_REFLECT   2

typedef enum __CRYPTO_METHOD {
    CRYPTO_METHOD_RSV = 0,
    CRYPTO_METHOD_RC4,
    CRYPTO_METHOD_SHA1,
    CRYPTO_METHOD_AES,
    CRYPTO_METHOD_DES,
    CRYPTO_METHOD_3DES,
    CRYPTO_METHOD_CRC, // /< CRC
    CRYPTO_METHOD_MD5 // /< MD5
}CRYPTO_METHOD;

 /**
 * The enum of the encryption/decryption way.
 */
typedef enum __CRYPTO_WAY {
    CRYPTO_WAY_ENCRYPT = 0, // /< Encryption
    CRYPTO_WAY_DECRYPT // /< Decryption
}CRYPTO_WAY;

typedef enum __CRYPTO_RNG_SWITCH {
    CRYPTO_RNG_SWITCH_16 = 0,
    CRYPTO_RNG_SWITCH_32
}CRYPTO_RNG_SWITCH;

 /**
 * The enum of the encryption/decryption mode, only for (aes des 3des).
 */
typedef enum __CRYPTO_MODE {
    CRYPTO_MODE_ECB = 0, // /< ECB
    CRYPTO_MODE_CBC,  // /< CBC
    CRYPTO_MODE_CTR, // /< CTR, only for AES
    CRYPTO_MODE_CMAC // /< MAC, only for AES
}CRYPTO_MODE;
 /**
 * The enum of the CRC type.
 */
typedef enum __CRYPTO_CRC_TYPE {
    CRYPTO_CRC_TYPE_8 = 0, // /< 8 bits CRC
    CRYPTO_CRC_TYPE_16_MODBUS, // /< 16 bits Modbus CRC
    CRYPTO_CRC_TYPE_16_CCITT, // /< 16 bits CCITT CRC
    CRYPTO_CRC_TYPE_32 // /< 32 bits CRC
}CRYPTO_CRC_TYPE;

 /**
 * The struct of the CRC context.
 */
typedef struct {
    u32 state; // /<  The initial value input and result value output.
    u8 type; // /< The type of CRC, refernec the CRYPTO_CRC_TYPE enum.
    u8 mode; // /< The mode of CRC, bit0 means output reflection and bit1 means input reflection.
}psCrcContext_t;

#if 1
typedef u32            hstm_digit;
typedef struct {
    int16    used, alloc, sign;
    hstm_digit *dp;
} hstm_int;

typedef struct {
#  ifdef HAVE_NATIVE_INT64
    uint64 length;
#  else
    uint32 lengthHi;
    uint32 lengthLo;
#  endif /* HAVE_NATIVE_INT64 */
    uint32 state[5], curlen;
    unsigned char buf[64];
} psSha1_t;
typedef struct {
#  ifdef HAVE_NATIVE_INT64
    uint64 length;
#  else
    uint32 lengthHi;
    uint32 lengthLo;
#  endif /* HAVE_NATIVE_INT64 */
    uint32 state[4], curlen;
    unsigned char buf[64];
} psMd5_t;

typedef struct {
    union {
        psSha1_t sha1;
        psMd5_t md5;
    } u;
    int32_t hashAlgId;
} psDigestContext_t;

# define AES_BLOCKLEN    16
# define AES_IVLEN       AES_BLOCKLEN
# define AES128_KEYLEN   16
# define AES192_KEYLEN   24
# define AES256_KEYLEN   32
# define DES3_BLOCKLEN    8
# define DES3_IVLEN      DES3_BLOCKLEN
# define DES3_KEYLEN     24

typedef struct {
    uint32_t skey[64];      /**< Key schedule (either encrypt or decrypt) */
    uint16_t rounds;        /**< Number of rounds */
    uint16_t type;          /**< PS_AES_ENCRYPT or PS_AES_DECRYPT (inverse) key */
} psAesKey_t;
typedef struct {
    psAesKey_t key;
    unsigned char IV[AES_BLOCKLEN];
} psAesCbc_t;

typedef struct {
    unsigned char state[256];
    uint32_t byteCount;
    unsigned char x;
    unsigned char y;
} psArc4_t;

typedef struct {
    uint32_t ek[3][32];
    uint32_t dk[3][32];
} psDes3Key_t;

typedef struct {
    psDes3Key_t key;
    unsigned char IV[DES3_BLOCKLEN];
    uint32_t blocklen;
} psDes3_t;

typedef union {
    psAesCbc_t aes;
    psArc4_t arc4;
    psDes3_t des3;
} psCipherContext_t;

#endif

struct wm_crypto_ctx {
    volatile u8 rsa_complete;
    volatile u8 gpsec_complete;
#ifndef CONFIG_KERNEL_NONE
    tls_os_sem_t *gpsec_lock;
#endif
};

/**
 * @defgroup System_APIs System APIs
 * @brief System APIs
 */

/**
 * @addtogroup System_APIs
 * @{
 */

/**
 * @defgroup HARD_CRYPTO_APIs HARD CRYPTO APIs
 * @brief hard crypto APIs
 */

/**
 * @addtogroup HARD_CRYPTO_APIs
 * @{
 */

/**
 * @brief              This function is used to stop random produce.
 *
 * @param[in]          None
 *
 * @retval             0             success
 * @retval             other     failed
 *
 * @note               None
 */
int tls_crypto_random_stop(void);

/**
 * @brief              This function initializes random digit seed and BIT number.
 *
 * @param[in]       seed         The random digit seed.
 * @param[in]       rng_switch     The random digit bit number.   (0: 16bit    1:32bit)
 *
 * @retval          0              success
 * @retval          other           failed
 *
 * @note                 None
 */
int tls_crypto_random_init(u32 seed, CRYPTO_RNG_SWITCH rng_switch);

/**
 * @brief              This function is used to get random digit content.
 *
 * @param[in]       out             Pointer to the output of random digit.
 * @param[in]       len             The random digit bit number will output.
 *
 * @retval          0              success
 * @retval          other           failed
 *
 * @note                 None
 */
int tls_crypto_random_bytes(unsigned char *out, u32 len);

/**
 * @brief              This function is used to generate true random number.
 *
 * @param[in]       out             Pointer to the output of random number.
 * @param[in]       len             The random number length.
 *
 * @retval          0              success
 * @retval          other           failed
 *
 * @note               None
 */
int tls_crypto_trng(unsigned char *out, u32 len);

/**
 * @brief              This function initializes a RC4 encryption algorithm,
 *                i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data.
 *
 * @param[in]       ctx         Pointer to the Cipher Context.
 * @param[in]       key         Pointer to the key.
 * @param[in]       keylen     the length of key.
 *
 * @retval          0          success
 * @retval          other       failed

 *
 * @note                 The first parameter ctx must be a structure which is allocated externally.
 *                  And all of Context parameters in the initializing methods should be allocated externally too.
 */
int tls_crypto_rc4_init(psCipherContext_t *ctx, const unsigned char *key, u32 keylen);

/**
 * @brief              This function encrypts a variable length data stream according to RC4.
 *                The RC4 algorithm it generates a "keystream"
 *                which is simply XORed with the plaintext to produce the ciphertext stream.
 *                Decryption is exactly the same as encryption.
 *                This function also decrypts a variable length data stream according to RC4.
 *
 * @param[in]  ctx    Pointer to the Cipher Context.
 * @param[in]  in    Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length.
 * @param[in]  out    Pointer to the resulting ciphertext data stream.
 * @param[in]   len    Length of the plaintext data stream in octets.
 *
 * @retval          0          success
 * @retval          other       failed
 *
 * @note                 None
 */
int tls_crypto_rc4(psCipherContext_t *ctx, unsigned char *in, unsigned char *out, u32 len);

/**
 * @brief              This function initializes a AES encryption algorithm,
 *                     i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data.
 *
 * @param[in]       ctx         Pointer to the Cipher Context.
 * @param[in]       IV         Pointer to the Initialization Vector
 * @param[in]       key         Pointer to the key.
 * @param[in]        keylen     the length of key.
 * @param[in]       cbc         the encryption mode, AES supports ECB/CBC/CTR modes.
 *
 * @retval          0          success
 * @retval          other       failed
 *
 * @note                 None
 */
int tls_crypto_aes_init(psCipherContext_t *ctx, const unsigned char *IV,
                        const unsigned char *key, u32 keylen, CRYPTO_MODE cbc);

/**
 * @brief            This function encrypts or decrypts a variable length data stream according to AES.
 *
 * @param[in]   ctx    Pointer to the Cipher Context.
 * @param[in]   in    Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length.
 * @param[in]   out    Pointer to the resulting ciphertext data stream.
 * @param[in]   len    Length of the plaintext data stream in octets.
 * @param[in]   dec    The cryption way which indicates encryption or decryption.
 *
 * @retval        0          success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_aes_encrypt_decrypt(psCipherContext_t *ctx, unsigned char *in,
                                   unsigned char *out, u32 len, CRYPTO_WAY dec);

/**
 * @brief            This function initializes a 3DES encryption algorithm,
 *                   i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data.
 *
 * @param[in]        ctx         Pointer to the Cipher Context.
 * @param[in]        IV         Pointer to the Initialization Vector
 * @param[in]        key         Pointer to the key.
 * @param[in]        keylen     the length of key.
 * @param[in]        cbc         the encryption mode, 3DES supports ECB/CBC modes.
 *
 * @retval        0          success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_3des_init(psCipherContext_t *ctx, const unsigned char *IV,
                         const unsigned char *key, u32 keylen, CRYPTO_MODE cbc);

/**
 * @brief            This function encrypts or decrypts a variable length data stream according to 3DES.
 *
 * @param[in]   ctx    Pointer to the Cipher Context.
 * @param[in]   in    Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length.
 * @param[in]   out    Pointer to the resulting ciphertext data stream.
 * @param[in]   len    Length of the plaintext data stream in octets.
 * @param[in]   dec    The cryption way which indicates encryption or decryption.
 *
 * @retval        0          success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_3des_encrypt_decrypt(psCipherContext_t *ctx, unsigned char *in,
                                    unsigned char *out, u32 len, CRYPTO_WAY dec);

/**
 * @brief            This function initializes a DES encryption algorithm,
 *                   i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data.
 *
 * @param[in]        ctx         Pointer to the Cipher Context.
 * @param[in]        IV         Pointer to the Initialization Vector
 * @param[in]        key         Pointer to the key.
 * @param[in]        keylen     the length of key.
 * @param[in]        cbc         the encryption mode, DES supports ECB/CBC modes.
 *
 * @retval        0          success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_des_init(psCipherContext_t *ctx, const unsigned char *IV,
                        const unsigned char *key, u32 keylen, CRYPTO_MODE cbc);

/**
 * @brief            This function encrypts or decrypts a variable length data stream according to DES.
 *
 * @param[in]    ctx  Pointer to the Cipher Context.
 * @param[in]    in   Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length.
 * @param[in]    out  Pointer to the resulting ciphertext data stream.
 * @param[in]    len  Length of the plaintext data stream in octets.
 * @param[in]    dec  The cryption way which indicates encryption or decryption.
 *
 * @retval        0          success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_des_encrypt_decrypt(psCipherContext_t *ctx, unsigned char *in,
                                   unsigned char *out, u32 len, CRYPTO_WAY dec);

/**
 * @brief            This function initializes a CRC algorithm,
 *                   i.e. fills the psCrcContext_t structure pointed to by ctx with necessary data.
 *
 * @param[in]        ctx         Pointer to the CRC Context.
 * @param[in]        key         The initialization key.
 * @param[in]        crc_type     The CRC type, supports CRC8/CRC16 MODBUS/CRC16 CCITT/CRC32
 * @param[in]        mode     Set input or outpu reflect.
 * @param[in]        dec         The cryption way which indicates encryption or decryption.
 *                see OUTPUT_REFLECT
 *                 see INPUT_REFLECT
 *
 * @retval        0        success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_crc_init(psCrcContext_t *ctx, u32 key, CRYPTO_CRC_TYPE crc_type, u8 mode);

/**
 * @brief            This function updates the CRC value with a variable length bytes.
 *                This function may be called as many times as necessary, so the message may be processed in blocks.
 *
 * @param[in]        ctx         Pointer to the CRC Context.
 * @param[in]        in         Pointer to a variable length bytes
 * @param[in]        len         The bytes 's length
 *
 * @retval        0        success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_crc_update(psCrcContext_t *ctx, unsigned char *in, u32 len);

/**
 * @brief            This function ends a CRC operation and produces a CRC value.
 *
 * @param[in]        ctx         Pointer to the CRC Context.
 * @param[in]        crc_val     Pointer to the CRC value.
 *
 * @retval        0        success
 * @retval        other    failed
 *
 * @note            None
 */
int tls_crypto_crc_final(psCrcContext_t *ctx, u32 *crc_val);

/**
 * @brief            This function initializes Message-Diggest context for usage in SHA1 algorithm,
 *                   starts a new SHA1 operation and writes a new Digest Context.
 *
 * @param[in]        md         Pointer to the SHA1 Digest Context.
 *
 * @retval        0        success
 * @retval        other    failed
 *
 * @note            None
 */
void tls_crypto_sha1_init(psDigestContext_t *md);

/**
 * @brief            Process a message block using SHA1 algorithm.
 *                This function performs a SHA1 block update operation. It continues an SHA1 message-digest operation,
 *                by processing InputLen-byte length message block pointed to by buf,
 *                and by updating the SHA1 context pointed to by md.
 *                This function may be called as many times as necessary, so the message may be processed in blocks.
 *
 * @param[in]        md        Pointer to the SHA1 Digest Context.
 * @param[in]      buf         InputLen-byte length message block
 * @param[in]      len         The buf 's length
 *
 * @returnl        None
 *
 * @note            None
 */
void tls_crypto_sha1_update(psDigestContext_t *md, const unsigned char *buf, u32 len);

/**
 * @brief            This function ends a SHA1 operation and produces a Message-Digest.
 *                This function finalizes SHA1 algorithm, i.e. ends an SHA1 Message-Digest operation,
 *                writing the Message-Digest in the 20-byte buffer pointed to by hash in according to
 *                the information stored in context.
 *
 * @param[in]        md        Pointer to the SHA1 Digest Context.
 * @param[in]        hash     Pointer to the Message-Digest
 *
 * @retval          20          success, return the hash size.
 * @retval          <0       failed

 *
 * @note            None
 */
int tls_crypto_sha1_final(psDigestContext_t *md, unsigned char *hash);

/**
 * @brief This function initializes Message-Diggest context for usage in MD5 algorithm,
 *        starts a new MD5 operation and writes a new Digest Context.
 *      This function begins a MD5 Message-Diggest Algorithm,
 *      i.e. fills the psDigestContext_t structure pointed to by md with necessary data.
 *      MD5 is the algorithm which takes as input a message of arbitrary length
 *      and produces as output a 128-bit "fingerprint" or "message digest" of the input.
 *      It is conjectured that it is computationally infeasible to produce two messages having the same message digest,
 *      or to produce any message having a given prespecified target message digest.
 *
 * @param[in]        md        MD5 Digest Context.
 *
 * @return        None
 *
 * @note            None
 */
void tls_crypto_md5_init(psDigestContext_t *md);

/**
 * @brief            Process a message block using MD5 algorithm.
 *                This function performs a MD5 block update operation. It continues an MD5 message-digest operation,
 *                by processing InputLen-byte length message block pointed to by buf,
 *                and by updating the MD5 context pointed to by md.
 *                This function may be called as many times as necessary, so the message may be processed in blocks.
 *
 * @param[in]        md        MD5 Digest Context.
 *  @param[in]      buf         InputLen-byte length message block
 *  @param[in]      len         The buf 's length
 *
 * @return        None
 *
 * @note            None
 */
void tls_crypto_md5_update(psDigestContext_t *md, const unsigned char *buf, u32 len);

/**
 * @brief            This function ends a MD5 operation and produces a Message-Digest.
 *                This function finalizes MD5 algorithm, i.e. ends an MD5 Message-Digest operation,
 *                writing the Message-Digest in the 16-byte buffer pointed to by hash in according
 *                to the information stored in context.
 *
 * @param[in]        md        MD5 Digest Context.
 * @param[in]        hash     the Message-Digest
 *
 * @retval          16          success, return the hash size.
 * @retval          <0       failed
 *
 * @note            None
 */
int tls_crypto_md5_final(psDigestContext_t *md, unsigned char *hash);

/**
 * @brief            This function implements the large module power multiplication algorithm.
 *                res = a**e (mod n)
 *
 * @param[in]        a         Pointer to a bignumber.
 * @param[in]        e         Pointer to a bignumber.
 * @param[in]      n         Pointer to a bignumber.
 * @param[out]      res         Pointer to the result bignumber.
 *
 * @retval          0          success
 * @retval          other       failed
 *
 * @note            None
 */
int tls_crypto_exptmod(hstm_int *a, hstm_int *e, hstm_int *n, hstm_int *res);

/**
 * @brief            This function initializes the encryption module.
 *
 * @param        None
 *
 * @retval          0          success
 * @retval          other       failed
 *
 * @note            None
 */
int tls_crypto_init(void);

/**
 * @}
 */

/**
 * @}
 */

#endif

